#include "../StdAfx.h"
#include "../MemPtr.h"

static const CLSID CLSID_UnknownJunction = { 0xfc0a77e6, 0x9d70, 0x4258, {0x97, 0x83, 0x6d, 0xab, 0x1d, 0x0f, 0xe3, 0x1e} };

struct CFileSysBindData : CComObjectRootEx<CComSingleThreadModel>,
                          IFileSystemBindData2
{
	DECLARE_NO_REGISTRY()
	DECLARE_NOT_AGGREGATABLE(CFileSysBindData)
	DECLARE_PROTECT_FINAL_CONSTRUCT()

	BEGIN_COM_MAP(CFileSysBindData)
		COM_INTERFACE_ENTRY(IFileSystemBindData2)
	END_COM_MAP()

	HRESULT FinalConstruct()
	{
		HRESULT hr;
		
		if (SUCCEEDED(hr = SetFindData(NULL)))
		{
			LARGE_INTEGER Zero = { 0 };

			if (SUCCEEDED(hr = SetFileID(Zero)))
			{
				hr = SetJunctionCLSID(CLSID_UnknownJunction);
			}
		}

		return hr;
	}

	IFACEMETHODIMP SetFindData(__in_opt CONST WIN32_FIND_DATAW * FindData)
	{
		if (FindData != NULL)
		{
			_FindData = *FindData;
		}
		else
		{
			::ZeroMemory(&_FindData, sizeof(_FindData));
		}

		return S_OK;
	}

	IFACEMETHODIMP GetFindData(__out WIN32_FIND_DATAW * FindData)
	{
		HRESULT hr = E_POINTER;

		if (FindData != NULL)
		{
			*FindData = _FindData;
			hr = S_OK;
		}

		return hr;
	}

	IFACEMETHODIMP SetFileID(LARGE_INTEGER FileID)
	{
		_FileID = FileID;
		return S_OK;
	}

	IFACEMETHODIMP GetFileID(__out PLARGE_INTEGER FileID)
	{
		HRESULT hr = E_POINTER;

		if (FileID != NULL)
		{
			*FileID = _FileID;
			hr = S_OK;
		}

		return hr;
	}

	IFACEMETHODIMP SetJunctionCLSID(REFIID ClassID)
	{
		_JunctionCLSID = ClassID;
		return S_OK;
	}

	IFACEMETHODIMP GetJunctionCLSID(__out LPCLSID ClassID)
	{
		HRESULT hr = E_POINTER;

		if (ClassID != NULL)
		{
			if (_JunctionCLSID == CLSID_UnknownJunction)
			{
				*ClassID = CLSID_NULL;
				hr = E_FAIL;
			}
			else
			{
				*ClassID = _JunctionCLSID;
				hr = S_OK;
			}
		}

		return hr;
	}

private:

	WIN32_FIND_DATAW _FindData;
	LARGE_INTEGER    _FileID;
	CLSID _JunctionCLSID;

};

STDMETHODIMP RegisterObjectParam(__in CComPtr<IBindCtx> & BindCtx, LPCWSTR Name, IUnknown * Value)
{
	static const size_t BufferSize = 1024;

	HRESULT hr;
	CComMemPtr<OLECHAR> Buffer;
	
	if (SUCCEEDED(hr = Buffer.Allocate(BufferSize)))
	{
		if (SUCCEEDED(hr = ::StringCchCopy(Buffer, BufferSize, Name)))
		{
			hr = BindCtx->RegisterObjectParam(Buffer, Value);
		}
	}
	
	return hr;
}

STDMETHODIMP CreateFileSysBindCtx(__in CONST WIN32_FIND_DATAW * FindData, __deref_out IBindCtx * * BindCtx)
{
	HRESULT hr = E_POINTER;

	if (BindCtx != NULL)
	{
		CComPtr<CFileSysBindData> FsBindData;

		if (SUCCEEDED(hr = CoCreate(FsBindData)))
		{
			if (SUCCEEDED(hr = FsBindData->SetFindData(FindData)))
			{
				CComPtr<IBindCtx> Result;

				if (SUCCEEDED(hr = ::CreateBindCtx(0, &Result)))
				{
					BIND_OPTS BindOpts = { sizeof(BindOpts) };
					BindOpts.grfMode = STGM_CREATE;

					if (SUCCEEDED(hr = Result->SetBindOptions(&BindOpts)))
					{
						if (SUCCEEDED(hr = RegisterObjectParam(Result, STR_FILE_SYS_BIND_DATA, FsBindData)))
						{
							*BindCtx = Result.Detach();
						}
					}
				}
			}
		}
	}

	return hr;
}

